package Concurrent.Coelho;

import Common.Coelho;
import Common.EstadoCoelho;
import Monitor.Prado;
import java.util.Random;


public class CoelhoConcorrente extends Coelho implements Runnable{

    private Prado field;

    public CoelhoConcorrente (Prado temp, int id, int Ef, int Es, int Ej, int Et, int line, int column, int nl, int nc) {
        super(id, Es + Ef/2, Es, Ej, Et, line, column, nl, nc);
        this.field = temp;

    }

    private void jump () {
        int dest = this.chooseJumpDest();

        switch(dest) {
            case 0:
                if(field.canJump(this.getId(), this.getX(), this.getY(), this.getX()-1, this.getY()-1)) {
                    this.setX(this.getX()-1);
                    this.setY(this.getY()-1);
                    break;
                }
            case 1:
                if(field.canJump(this.getId(), this.getX(), this.getY(), this.getX(), this.getY()-1)) {
                    this.setY(this.getY()-1); break;
                }
            case 2:
                if(field.canJump(this.getId(), this.getX(), this.getY(), this.getX()+1, this.getY()-1)) {
                    this.setX(this.getX()+1);
                    this.setY(this.getY()-1);
                    break;
                }
            case 3:
                if(field.canJump(this.getId(), this.getX(), this.getY(), this.getX()+1, this.getY())) {
                    this.setX(this.getX()+1); break;
                }
            case 4:
                if(field.canJump(this.getId(), this.getX(), this.getY(), this.getX()+1, this.getY()+1)) {
                    this.setX(this.getX()+1);
                    this.setY(this.getY()+1);
                    break;
                }
            case 5:
                if(field.canJump(this.getId(), this.getX(), this.getY(), this.getX(), this.getY()+1)) {
                    this.setY(this.getY()+1); break;
                }
            case 6:
                if(field.canJump(this.getId(), this.getX(), this.getY(), this.getX()-1, this.getY()+1)) {
                    this.setX(this.getX()-1);
                    this.setY(this.getY()+1);
                    break;
                }
            case 7:
                if(field.canJump(this.getId(), this.getX(), this.getY(), this.getX()-1, this.getY())) {
                    this.setX(this.getX()-1); break;
                }
        }
        this.decEnergy(this.getE_j());
    }

    private void die () {
        this.setState(EstadoCoelho.Dead);
        field.informCoelho(this.getId(), EstadoCoelho.Dead, this.getX(), this.getY());
        super.log.Log("Coelho " + this.getId() + " morreu na posicao X-Y: " + this.getX() + "-" + this.getY());
    }

    private void get_hungry () {
        this.setState(EstadoCoelho.Searching);
        field.informCoelho(this.getId(), EstadoCoelho.Searching, this.getX(), this.getY());
    }

    private void eat_the_carrot () {
        this.setEnergia(this.getEnergia() + field.carrotEnergy(this.getX(), this.getY()) / 2);
        //log.Log("Coelho comeu, nova energia: " + this.getEnergia());
    }

    private void get_satiated () {
        this.setState(EstadoCoelho.Wandering);
        field.informCoelho(this.getId(), EstadoCoelho.Wandering, this.getX(), this.getY());
    }

    public void run () {
        Random gerador = new Random(this.getId() + 10);

        //State Machine
        while(this.getState() != EstadoCoelho.Dead)
        {
            switch(this.getState())
            {
                case Wandering:
                    if(this.getEnergia() > this.getE_s())
                        this.jump();
                    else
                        this.get_hungry();
                    break;

                case Searching:
                    if(this.getEnergia() <= 0)
                        this.die();
                    else {
                        if(this.getEnergia() > this.getE_s())
                            this.get_satiated();
                        else {
                            if(field.hasCarrot(this.getX(), this.getY()))
                                this.eat_the_carrot();
                            else
                                this.jump();
                        }
                    }
            }
            if(field.getEndFlag()) {
                this.setState(EstadoCoelho.Dead);
                field.informCoelho(this.getId(), EstadoCoelho.Dead, this.getX(), this.getY());
            }
            try {
                Thread.sleep(gerador.nextInt(201) + 900);
                this.decEnergy(this.getE_t());
            }
            catch(InterruptedException ex) {
                //Report Exception in Log File --> print ex.getMessage()
                return;
            }
        }

    }

    //Métodos Auxiliares --> Privados
    /**
     * Função que efectua decrementa a energia do coelho conforme o valor especificado.
     * @param decVal -> valor a decrementar
     */
    private void decEnergy(int decVal) {
        this.setEnergia(this.getEnergia() - decVal);
        //log.Log("Perdeu energia, nova energia: " + this.getEnergia());
    }

    /**
     * Função que escolhe, aleatoriamente, a direcção de salto do coelho.
     * 0 - NW
     * 1 - N
     * 2 - NE
     * 3 - E            0   1   2
     * 4 - SE           7       3
     * 5 - S            6   5   4
     * 6 - SW
     * 7 - W
     * @return direcção do salto
     */
    private int chooseJumpDest() {
        Random gen = new Random();
        int direction;

        if(this.getX() == 0)
        {
            if(this.getY() == 0)
                direction = gen.nextInt(3) + 3;
            else
            {
                if(this.getY() == (this.getNL() - 1))
                    direction = gen.nextInt(3) + 1;
                else
                    direction = gen.nextInt(5) + 1;
            }
        }
        else
        {
            if(this.getX() == (this.getNC() - 1))
            {
                if(this.getY() == 0)
                    direction = gen.nextInt(3) + 5;
                else
                {
                    if(this.getY() == (this.getNL() - 1))
                    {
                        direction = gen.nextInt(3);
                        if(direction == 2)
                            direction = 7;
                    }
                    else
                    {
                        direction = gen.nextInt(5);
                        if(direction > 1)
                            direction += 3;
                    }
                }
            }
            else
            {
                if(this.getY() == 0)
                    direction = gen.nextInt(5) + 3;
                else
                {
                    if(this.getY() == this.getNL() - 1)
                    {
                        direction = gen.nextInt(5);
                        if(direction == 4)
                            direction = 7;
                    }
                    else
                        direction = gen.nextInt(8);
                }
            }
        }
        return direction;
    }

}

